#!/bin/sh
#
# Copyright (c) 2009-2011,2013 LAAS/CNRS
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software for any purpose
# with or without   fee is hereby granted, provided   that the above  copyright
# notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS  SOFTWARE INCLUDING ALL  IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR  BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR  ANY DAMAGES WHATSOEVER RESULTING  FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION,   ARISING OUT OF OR IN    CONNECTION WITH THE USE   OR
# PERFORMANCE OF THIS SOFTWARE.
#
#                                             Anthony Mallet on Sun Nov 15 2009
#
: ${ECHO:=echo}
: ${ECHO_N:=echo -n}
: ${TEST:=test}
: ${SED:=sed}
: ${STTY:=stty}
: ${TPUT:=tput}

self="${0##*/}"

# Process optional arguments
#
nofilter=
logfile=
log=noverb
status=noverb
nolog=verb
nostatus=:
interactive=
raw=

usage() {
    ${ECHO} 1>&2 "usage: $self [-n] [-v] [-i] [-l file] [--] arg0 [arg...]"
}

while ${TEST} $# -gt 0; do
    case "$1" in
	-v)     log=verb; nolog=noverb; status=: nostatus=noverb; shift ;;
	-i)     interactive='[ESC toggles verbosity]'; shift;;
	-n)     nofilter=yes; interactive=; log=verb; nolog=verb; status=:
                shift;;
	-l)     logfile=$2; shift 2;;
	-r)     raw=yes; shift;;

        -c|--)  shift; break ;;
        -*)     ${ECHO} 1>&2 "$self: unknown option -- ${1#-}"
                usage
                exit 1
                ;;
        *)      break ;;
    esac
done

# magic shortcut for internal robotpkg stuff that do not need logging
case "$1" in ":"*) eval "$@"; exit $?;; esac

# No filter + no log mode: just eval the command line
#
if ${TEST} -n "$nofilter"; then
    if ${TEST} -z "$logfile"; then
        eval "$@"; exit $?
    fi
fi


# Interactive message strings
#
if ${TEST} -t 1; then
    sc=`${TPUT} sc`
    rc=`${TPUT} rc`
    el=`${TPUT} el 2>/dev/null || ${TPUT} ce 2>/dev/null`
    civis=`${TPUT} civis 2>/dev/null || ${TPUT} vi 2>/dev/null`
    cnorm=`${TPUT} cnorm 2>/dev/null || ${TPUT} ve 2>/dev/null`

    statuslinea=' -(o     )-'
    statuslineb=' -( o    )-'
    statuslinec=' -(  o   )-'
    statuslined=' -(   o  )-'
    statuslinee=' -(    o )-'
    statuslinef=' -(     o)-'
    statuslineg=' -(     o)-'
    statuslineh=' -(    o )-'
    statuslinei=' -(   o  )-'
    statuslinej=' -(  o   )-'
    statuslinek=' -( o    )-'
    statuslinel=' -(o     )-'
    statuslinem=' -(o     )-'
    statuslist='a b c d e f g h i j k l m'

    MSG=${ECHO_N}
else
    MSG=:
fi


# Log functions
#
verb() { ${ECHO} "$@"; }

if ${TEST} -t 1; then
    noverb() {
	set -- $statuslist
	i=$1
	eval ${MSG} '"${sc}Processing $interactive $statusline'$i'$el$rc"'
	shift
	statuslist="$@ $i"
    }
else
    noverb() { :;}
fi


if ${TEST} -n "$logfile"; then
    exec 6>>$logfile
    tofile() { ${ECHO} "$@" >&6; }
else
    tofile() { :;}
fi


# Tee-like output filter
#
filter() {
    while read l; do
        case $l in '=> '*) ${ECHO} $el$l; $status;; *) $log "$l";; esac
        tofile "$l"
        esc <&3
    done
}

errfilter() {
    while read l; do
        ${ECHO} "$el$l"; $status
        tofile "$l"
    done
}

# Run arguments with empty stdin and exit status on fd 4
#
run() {
    ( eval "$@" </dev/null 3>&- 4>&- 5>&- )
    echo $? >&4
}



# Read ESC key, nonblocking
#
if ${TEST} -n "${interactive}" -a -t 0; then
    esc() {
	read key
	test "$key" = "" && {
            s=$log; log=$nolog; nolog=$s;
            s=$status; status=$nostatus; nostatus=$s;
        }
    }
else
    esc() { :;}
fi


# Spawn program
#
trap "${MSG} $el$cnorm; stty icanon echo; exit 127" INT QUIT ABRT
test -n "${civis}" && ${MSG} ${civis}

exec 3<&0 4>&1 5>&1
${TEST} -t 3 && stty -icanon -echo min 0 <&3 2>/dev/null

if ${TEST} -z "$raw"; then
  tofile "Running $@"
fi;
result=`{ { run "$@" | filter; } 2>&1 >&5 | errfilter; } 4>&1 >&5`
: ${result:=255}

${TEST} -t 3 && stty icanon echo <&3 2>/dev/null
exec 3>&- 4>&- 5>&-

${MSG} $el$cnorm

# Log last lines in case of error
#
${TEST} -n "$logfile" && ${TEST} "$result" -ne 0 && {
    ${ECHO}
    ${ECHO} "An unexpected error occured. " \
      "The last 10 log lines are shown below." >&2
    ${SED} -e ':a' -e '$q;N;11,$D;ba' < $logfile | ${SED} -e 's/^/| /' >&2
    ${ECHO}
    ${ECHO} "For details or bug reports, check the complete log file in:" >&2
    ${ECHO} "$logfile" >&2
}

exit "$result"
